Since 2015, JavaScript has improved immensely.
It’s much more pleasant to use it now than ever.
In this article, we’ll look at metaprogramming with JavaScript proxies.
Metaprogramming
Metaprogramming is writing code that processes our app-level code.
App-level code is code that takes user input and returns output for the user.
There’re various kinds of metaprogramming.
One kind is introspection. This is where we have read-only access to the structure of a program.
Self-modification is where we change the program’s structure.
Intercession is where we redefine the semantics of some language operation.
An example of introspection in Javascript is using Object.keys()
to get the keys of the object.
An example of self-modification is to move a property from one place to another.
For instance, we can write:
function moveProperty(source, prop, target) {
target[prop] = source[prop];
delete source[prop];
}
to move the prop
property from the source
object to the target
object.
Then we delete the source
object’s prop
property.
Proxies let us do intercession with our JavaScript app.
We can change how object operations are done with it.
Proxies
Proxies bring intercession to JavaScript.
We can do many operations with an object.
We can get the property value, and we can set it.
Also, we can check if property is in an object.
We can customize these operations with proxies.
For example, we can write:
const target = {};
const handler = {
get(target, propKey, receiver) {
console.log(target, propKey, receiver);
return 'foo';
}
};
const proxy = new Proxy(target, handler);
console.log(proxy.bar)
We create an empty target
object that we use with the proxy.
And the handler
object has a get
method that lets us return a value for the properties given.
target
is the object we passed into the first argument of the Proxy
constructor.
propKey
is the property name.
So if we get the value of proxy.bar
or any other property of proxy
, we get 'foo'
returned.
We can also change the way that we check if a property exists in an object.
For example, we can write:
const target = {};
const handler = {
has(target, propKey) {
console.log(propKey);
return true;
}
};
const proxy = new Proxy(target, handler);
console.log('bar' in proxy);
console.log('foo' in proxy);
Then we get the target
and propKey
parameters, which hold the same values as the ones in get
.
We returned true
so whenever we use the in
operator with it, it returns true
.
The set
method can let us change how objects are set.
Intercepting Method Calls
We can intercept method calls by calling the methods in object properties our way.
For instance, we can write:
const target = {
foo(text) {
return `text ${text}`;
}
};
const handler = {
get(target, propKey, receiver) {
const origMethod = target[propKey];
return function(...args) {
const result = origMethod.apply(this, args);
console.log(JSON.stringify(args), JSON.stringify(result));
return result;
};
}
};
const proxy = new Proxy(target, handler);
console.log(proxy.foo('baz'));
We call the target.foo
method within th get
method of the handler
.
Inside the get
method, we call the method with the arguments but with this
being the proxy instead of the target
object.
This way, we can change the behavior of the method call.
We logged the arguments and the results of it.
It’s handy for logging method calls.
Conclusion
Proxies let us run control how object properties are get and set.
We can also change the way that properties are check.